package com.ocean.recommendationserver.realtimerecommend.crawl;

import com.alibaba.fastjson.JSONObject;
import okhttp3.*;
import okhttp3.Request.Builder;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import javax.annotation.Nullable;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;

public class MyOkHttpUtil {
    private HashMap<String, Map<String, Cookie>> cookieStore = new HashMap<>();
    private static final String CONSTANT_USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36";
    private String userAgent = null;
    private String connection = null;
    private String accept = null;
    private String acceptEncoding = null;
    private String acceptLanguage = null;
    private String charsert = null;
    private OkHttpClient client = null;

    private JSONObject proxyParam;
    private static BaseAuthenticator authenticator = new BaseAuthenticator();

    static {
        Authenticator.setDefault(authenticator);
    }

    public static MyOkHttpUtil create() {
        return new MyOkHttpUtil(null);
    }

    public static MyOkHttpUtil create(JSONObject proxy) {
        return new MyOkHttpUtil(proxy);
    }

    private static void okHttpTrust(OkHttpClient.Builder builder) {
        try {
            // Create a trust manager that does not validate certificate chains
            final TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }

                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    }
            };

            // Install the all-trusting trust manager
            final SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            // Create an ssl socket factory with our all-trusting manager
            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
            //如果使用builder.sslSocketFactory(sslSocketFactory),构建okhttp的时候就没有信任所有证书这一步
//            builder.sslSocketFactory(sslSocketFactory);
            builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
            builder.hostnameVerifier((hostname, session) -> true);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public MyOkHttpUtil(JSONObject proxyParam) {
        connection = WebClientHeader.getConnection();
        accept = WebClientHeader.getAccept();
        acceptEncoding = WebClientHeader.getAcceptEncoding();
        acceptLanguage = WebClientHeader.getAcceptLanguage();
        charsert = WebClientHeader.getCharsert();
        userAgent = WebClientHeader.getUserAgent();
        this.proxyParam = proxyParam;
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        if (proxyParam != null) {
            String protocol = proxyParam.getString("protocol");
            InetSocketAddress addr = new InetSocketAddress(proxyParam.getString("ip"), proxyParam.getIntValue("port"));
            if(protocol != null && protocol.equalsIgnoreCase(Proxy.Type.HTTP.name())){
                builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyParam.getString("ip"), proxyParam.getIntValue("port"))));
                builder.proxyAuthenticator(new okhttp3.Authenticator() {
                    @Nullable
                    @Override
                    public Request authenticate(@Nullable Route route, Response response) throws IOException {
                        //设置代理服务器账号密码
                        String credential = Credentials.basic(proxyParam.getString("user"), proxyParam.getString("pwd"));
                        return response.request().newBuilder()
                                .header("Proxy-Authorization", credential)
                                .build();
                    }
                });
            }else {
                Proxy sockProxy = new Proxy(Proxy.Type.SOCKS, addr);
                authenticator.add(proxyParam.getString("ip"), proxyParam.getIntValue("port"), proxyParam.getString("user"),
                        proxyParam.getString("pwd"));
                builder.proxy(sockProxy);
            }
        }
        okHttpTrust(builder);
        client = builder
                .cookieJar((new CookieJar() {
                    @Override
                    public void saveFromResponse(HttpUrl httpUrl, List<Cookie> list) {
                        Map<String, Cookie> cookies = cookieStore.get(httpUrl.host());
                        if (cookies == null) {
                            cookies = new HashMap<>();
                        }
                        if (list != null) {
                            for (Cookie cookie : list) {
                                cookies.put(cookie.name(), cookie);
                            }
                        }
                        cookieStore.put(httpUrl.host(), cookies);
                    }
                    @Override
                    public List<Cookie> loadForRequest(HttpUrl httpUrl) {
                        Map<String, Cookie> cookies = cookieStore.get(httpUrl.host());
                        return cookies != null && cookies.size() > 0 ? new ArrayList<Cookie>(cookies.values()) : new ArrayList<Cookie>();
                    }
                }))
                .connectTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .followRedirects(true)
                .build();
    }


    public String httpGet(String url, String cookie) throws Exception {
        BufferedReader reader = null;
        Builder builder = new Builder().url(url);
        if (StringUtils.isNotBlank(cookie)) {
            builder = builder.header("Cookie", cookie);
        }
        Request request = builder
                .get()
                .addHeader("User-Agent", userAgent)
                .addHeader("Accept", accept)
                .addHeader("Cache-Control", "no-cache")
                .addHeader("Host", getCodeUrl(url).replaceFirst("(https|http)://", ""))
                .addHeader("Accept-Encoding", acceptEncoding)
                .addHeader("Connection", connection)
                .addHeader("cache-control", "no-cache")
                .build();
        Response response = client.newCall(request).execute();
        if ("gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
            GZIPInputStream gZIPInputStream = new GZIPInputStream(response.body().byteStream());
            reader = new BufferedReader(new InputStreamReader(gZIPInputStream));
        } else {
            reader = new BufferedReader(new InputStreamReader(response.body().byteStream()));
        }
        String result = IOUtils.toString(reader);
        response.close();
        return result;
    }

    public String httpGetWithQueryString(String url, String cookie, Map<String, Object> params) throws Exception {
        BufferedReader reader = null;
        Builder builder = new Builder().url(url);
        if (StringUtils.isNotBlank(cookie)) {
            builder = builder.header("Cookie", cookie);
        }
        String paramsStr = "";
        if (params != null && !params.isEmpty()) {
            for (Entry<String, Object> entry : params.entrySet()) {
                paramsStr = paramsStr.concat(entry.getKey()).concat("=").concat((String)entry.getValue()).concat("&");
            }
            paramsStr = paramsStr.substring(0, paramsStr.length() - 1);
        }
        url = url + "?" + paramsStr;
        Request request = builder
                .get()
                .addHeader("User-Agent", userAgent)
                .addHeader("Accept", accept)
                .addHeader("Cache-Control", "no-cache")
                .addHeader("Host", getCodeUrl(url).replaceFirst("(https|http)://", ""))
                .addHeader("Accept-Encoding", acceptEncoding)
                .addHeader("Connection", connection)
                .addHeader("cache-control", "no-cache")
                .build();
        Response response = client.newCall(request).execute();
        if ("gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
            GZIPInputStream gZIPInputStream = new GZIPInputStream(response.body().byteStream());
            reader = new BufferedReader(new InputStreamReader(gZIPInputStream));
        } else {
            reader = new BufferedReader(new InputStreamReader(response.body().byteStream()));
        }
        String result = IOUtils.toString(reader);
        cookieStore.clear();
        response.close();
        return result;
    }

    public String httpPost(String url, Map<String, Object> params, String cookie, String xToken) throws Exception {
        BufferedReader reader = null;
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        String paramsStr = "";
        if (params != null && !params.isEmpty()) {
            for (Entry<String, Object> entry : params.entrySet()) {
                paramsStr = paramsStr.concat(entry.getKey()).concat("=").concat((String)entry.getValue()).concat("&");
            }
            paramsStr = paramsStr.substring(0, paramsStr.length() - 1);
        }
        RequestBody body = RequestBody.create(mediaType, paramsStr);
        Builder builder = new Builder().url(url);
        if (StringUtils.isNotBlank(cookie)) {
            builder = builder.header("Cookie", cookie);
        }
        if(StringUtils.isNotBlank(xToken)){
            builder = builder.header("X-CSRF-Token", xToken);
        }
        Request request = builder
                .post(body)
                .addHeader("User-Agent", userAgent)
                .addHeader("Accept", accept)
                .addHeader("Cache-Control", "no-cache")
                .addHeader("Host", getCodeUrl(url).replaceFirst("(https|http)://", ""))
                .addHeader("Accept-Encoding", acceptEncoding)
                .addHeader("Connection", connection)
                .addHeader("cache-control", "no-cache")
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .build();
        Response response = client.newCall(request).execute();
        if ("gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
            GZIPInputStream gZIPInputStream = new GZIPInputStream(response.body().byteStream());
            reader = new BufferedReader(new InputStreamReader(gZIPInputStream));
        } else {
            reader = new BufferedReader(new InputStreamReader(response.body().byteStream()));
        }
        String result = IOUtils.toString(reader);
        cookieStore.clear();
        response.close();
        return result;
    }

    public Map<Integer, String> login(String url, Map<String, String> params) throws Exception {
        Map<Integer, String> loginResult = new HashMap<>();
        BufferedReader reader = null;
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        String paramsStr = "";
        if (params != null && !params.isEmpty()) {
            for (Entry<String, String> entry : params.entrySet()) {
                paramsStr = paramsStr.concat(entry.getKey()).concat("=").concat(entry.getValue()).concat("&");
            }
            paramsStr = paramsStr.substring(0, paramsStr.length() - 1);
        }
        RequestBody body = RequestBody.create(mediaType, paramsStr);
        Request request = new Builder()
                .url(url)
                .post(body)
                .addHeader("User-Agent", userAgent)
                .addHeader("Accept", accept)
                .addHeader("Cache-Control", "no-cache")
                .addHeader("Host", getCodeUrl(url).replaceFirst("(https|http)://", ""))
                .addHeader("Accept-Encoding", acceptEncoding)
                .addHeader("Connection", connection)
                .addHeader("cache-control", "no-cache")
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .build();
        cookieStore.clear();
        Response response = client.newCall(request).execute();

        if ("gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
            GZIPInputStream gZIPInputStream = new GZIPInputStream(response.body().byteStream());
            reader = new BufferedReader(new InputStreamReader(gZIPInputStream));
        } else {
            reader = new BufferedReader(new InputStreamReader(response.body().byteStream()));
        }
        String cookie = "";
        Map<String, Cookie> cookieMap = cookieStore.get(getCodeUrl(url).replaceFirst("(https|http)://", ""));
        for (Entry<String, Cookie> entry : cookieMap.entrySet()) {
            cookie = cookie.concat(entry.getValue().toString()).concat("; ");
        }
        cookie = cookie.substring(0, cookie.length() - 2);
        response.close();
        return loginResult;
    }


    public String httpGetAssignUserAgent(String url, String cookie) {
        String result = null;
        try {
            BufferedReader reader = null;
            Builder builder = new Builder().url(url);
            if (StringUtils.isNotBlank(cookie)) {
                builder = builder.header("Cookie", cookie);
            }
            Request request = builder
                    .get()
                    .addHeader("User-Agent", CONSTANT_USER_AGENT)
                    .addHeader("Accept", accept)
                    .addHeader("Cache-Control", "no-cache")
                    .addHeader("Host", getCodeUrl(url).replaceFirst("(https|http)://", ""))
                    .addHeader("Accept-Encoding", acceptEncoding)
                    .addHeader("Connection", connection)
                    .addHeader("cache-control", "no-cache")
                    .build();
            Response response;
            response = client.newCall(request).execute();
            if ("gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
                GZIPInputStream gZIPInputStream = new GZIPInputStream(response.body().byteStream());
                reader = new BufferedReader(new InputStreamReader(gZIPInputStream));
            } else {
                reader = new BufferedReader(new InputStreamReader(response.body().byteStream()));
            }
            result = IOUtils.toString(reader);
            response.body().close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public String httpPostAssignUserAgent(String url, Map<String, String> params, String cookie) {
        String result = null;
        try {
            BufferedReader reader = null;
            MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
            String paramsStr = "";
            if (params != null && !params.isEmpty()) {
                for (Entry<String, String> entry : params.entrySet()) {
                    paramsStr = paramsStr.concat(entry.getKey()).concat("=").concat(entry.getValue()).concat("&");
                }
                paramsStr = paramsStr.substring(0, paramsStr.length() - 1);
            }
            RequestBody body = RequestBody.create(mediaType, paramsStr);
            Builder builder = new Builder().url(url);
            if (StringUtils.isNotBlank(cookie)) {
                builder = builder.header("Cookie", cookie);
            }
            Request request = builder
                    .post(body)
                    .addHeader("User-Agent", CONSTANT_USER_AGENT)
                    .addHeader("Accept", accept)
                    .addHeader("Cache-Control", "no-cache")
                    .addHeader("Host", getCodeUrl(url).replaceFirst("(https|http)://", ""))
                    .addHeader("Accept-Encoding", acceptEncoding)
                    .addHeader("Connection", connection)
                    .addHeader("cache-control", "no-cache")
                    .addHeader("Content-Type", "application/x-www-form-urlencoded")
                    .build();
            Response response = client.newCall(request).execute();
            if ("gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
                GZIPInputStream gZIPInputStream = new GZIPInputStream(response.body().byteStream());
                reader = new BufferedReader(new InputStreamReader(gZIPInputStream));
            } else {
                reader = new BufferedReader(new InputStreamReader(response.body().byteStream()));
            }
            result = IOUtils.toString(reader);
            response.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public String loginAssignUserAgent(String url, Map<String, String> params, String initCookie) {
        String cookie = "";
        try {
            MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
            String paramsStr = "";
            if (params != null && !params.isEmpty()) {
                for (Entry<String, String> entry : params.entrySet()) {
                    paramsStr = paramsStr.concat(entry.getKey()).concat("=").concat(entry.getValue()).concat("&");
                }
                paramsStr = paramsStr.substring(0, paramsStr.length() - 1);
            }
            RequestBody body = RequestBody.create(mediaType, paramsStr);
            Builder builder = new Builder().url(url);
            if (StringUtils.isNotBlank(initCookie)) {
                builder = builder.header("Cookie", initCookie);
            }
            Request request = builder
                    .post(body)
                    .addHeader("User-Agent", CONSTANT_USER_AGENT)
                    .addHeader("Accept", accept)
                    .addHeader("Cache-Control", "no-cache")
                    .addHeader("Host", getCodeUrl(url).replaceFirst("(https|http)://", ""))
                    .addHeader("Accept-Encoding", acceptEncoding)
                    .addHeader("Connection", connection)
                    .addHeader("cache-control", "no-cache")
                    .addHeader("Content-Type", "application/x-www-form-urlencoded")
                    .build();
            Response execute = client.newCall(request).execute();

            Map<String, Cookie> cookieMap = cookieStore.get(getCodeUrl(url).replaceFirst("(https|http)://", ""));
            for (Entry<String, Cookie> entry : cookieMap.entrySet()) {
                cookie = cookie.concat(entry.getValue().toString()).concat("; ");
            }
            cookie = cookie.substring(0, cookie.length() - 2);
            execute.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cookie;
    }

    public JSONObject httpPost(String url, Map<String, String> params) throws Exception {
        BufferedReader reader = null;
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        String paramsStr = "";
        if (params != null && !params.isEmpty()) {
            for (Entry<String, String> entry : params.entrySet()) {
                paramsStr = paramsStr.concat(entry.getKey()).concat("=").concat(entry.getValue()).concat("&");
            }
            paramsStr = paramsStr.substring(0, paramsStr.length() - 1);
        }
        RequestBody body = RequestBody.create(mediaType, paramsStr);
        Request request = new Builder()
                .url(url)
                .post(body)
                .addHeader("User-Agent", CONSTANT_USER_AGENT)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .addHeader("Accept", accept)
                .addHeader("Cache-Control", "no-cache")
                .addHeader("Host", getCodeUrl(url).replaceFirst("(https|http)://", ""))
                .addHeader("Accept-Encoding", acceptEncoding)
                .addHeader("Connection", connection)
                .addHeader("cache-control", "no-cache")
                .build();
        Response response = client.newCall(request).execute();
        if ("gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
            GZIPInputStream gZIPInputStream = new GZIPInputStream(response.body().byteStream());
            reader = new BufferedReader(new InputStreamReader(gZIPInputStream));
        } else {
            reader = new BufferedReader(new InputStreamReader(response.body().byteStream()));
        }

        String cookie = "";
        Map<String, Cookie> cookieMap = cookieStore.get(getCodeUrl(url).replaceFirst("(https|http)://", ""));
        for (Entry<String, Cookie> entry : cookieMap.entrySet()) {
            cookie = cookie.concat(entry.getValue().toString()).concat("; ");
        }
        cookie = cookie.substring(0, cookie.length() - 2);

        JSONObject result = new JSONObject();
        result.put("content", IOUtils.toString(reader));
        result.put("cookie", cookie);
        response.close();
        return result;
    }

    /**
     * 获取重定向地址
     *
     * @param url
     * @param cookie
     * @return
     * @throws Exception
     * @author zhuangyilian
     * @date 2020年9月22日
     */
    public String getRedictUrl(String url, String cookie) {
        Builder builder = new Builder().url(url);
        if (StringUtils.isNotBlank(cookie)) {
            builder = builder.header("Cookie", cookie);
        }
        Request request = builder
                .get()
                .addHeader("User-Agent", userAgent)
                .addHeader("Accept", accept)
                .addHeader("Cache-Control", "no-cache")
                .addHeader("Host", getCodeUrl(url).replaceFirst("(https|http)://", ""))
                .addHeader("Accept-Encoding", acceptEncoding)
                .addHeader("Connection", connection)
                .addHeader("cache-control", "no-cache")
                .build();
        try {
            Response response = client.newCall(request).execute();
            String rurl = response.request().url().toString();
            response.close();
            return rurl;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String getCodeUrl(String url) {
        String codeurl = PatternUtil.match(url, "((https|http)://\\S+?/)", 1, 1);
        if (codeurl == null) {
            codeurl = url;
        }
        if (codeurl.endsWith("/")) {
            codeurl = codeurl.substring(0, codeurl.length() - 1);
        }
        return codeurl;
    }

    public JSONObject getProxyParam() {
        return proxyParam;
    }
}
